package main

import (
	"bufio"
	"crypto/ecdsa"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"

	"gitee.com/thubcc/blockchain/p2p"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
	ethp2p "github.com/ethereum/go-ethereum/p2p"
	"github.com/ethereum/go-ethereum/p2p/enode"
	"github.com/ethereum/go-ethereum/params"
)

var (
	logRec  = make(map[string]string)
	currentRec  = make(map[string]string)
	nodes   = make([]*ethp2p.Server,0)
	argNum  = flag.Int("n",2,"start node")
	argFile = flag.String("f",".ethereum/node","node json")
)

type record struct {
	Addr string `json:"addr"`
	ID   string `json:"id"`
}

func processIp(i int,p *record) {
	if i%10000 == 9999 {
		dumpToFile(i/10000)
		fmt.Printf("%7d %s %s\n",i,p.ID,p.Addr)
	}
	laddr,lok := logRec[p.ID]
	caddr,cok := currentRec[p.ID]
	if (lok && laddr!="") || (cok && caddr!=""){
		return
	}
	if p.ID == "" || p.Addr == "" {
		return
	}
	currentRec[p.ID] = p.Addr
}

func LogProcessor(r *io.PipeReader,restart chan struct{}) {
	defer close(restart)
	reader := bufio.NewReader(r)
	for i:=0;;i++ {
		line,err := reader.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			return
		}
		var p = record{}
		err = json.Unmarshal([]byte(line), &p)
		if err == nil {
			processIp(i,&p)
		}
	}
}

func GenKey(match,len int) *ecdsa.PrivateKey {
	for{
		asymKey,err := crypto.GenerateKey()
		if err != nil {
			fmt.Printf("gen node kry: %v\n",err)
		}
		id := enode.PubkeyToIDV4(&asymKey.PublicKey)
		var hi = int(id[0])
		mask := 1<<len
		mask--
		if (hi>>(8-len))&mask == match {
			return asymKey
		}
	}
}

func Log2(i int) int {
	r := 0
	for{
		if i==0 {
			return r
		}
		i=i>>1
		r++
	}
}

func dumpToFile(i int) {
	buf,err := json.MarshalIndent(currentRec,"","  ")
	if err != nil {
		fmt.Println("Dump to file error")
	}
	fn := fmt.Sprintf("%s%05d.json",*argFile,i)
	if err = ioutil.WriteFile(fn,buf,0755); err!=nil {
		fmt.Println("write to file error")
	}
	for k ,v := range currentRec { logRec[k] = v } 
	currentRec = make(map[string]string)
}

func prepareNode(n int) {
	var peers = []*enode.Node{}
	for _,mainnode := range params.MainnetBootnodes {
		peer := enode.MustParse(mainnode)
		peers = append(peers,peer)
	}
	
	len := Log2(n-1)
	for i:=0;i<n;i++ {
		key := GenKey(i,len)
		server,err := p2p.NewNullNode(peers,key)
		if err != nil {
			fmt.Println("init p2p failure",err)
		} else {
			fmt.Println("Setup p2p Server")
		}
		nodes = append(nodes,server)
	}
}

func main() {
	flag.Parse()
	r,w := io.Pipe()
	logHandler := log.StreamHandler(w, log.JSONFormat())
	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(6),logHandler))
	restart := make(chan struct{})
	go LogProcessor(r,restart)
	
	prepareNode(*argNum)
	for _,v := range nodes {
		v.Start()
	}

	defer func() {
		for _,v := range nodes {
			v.Stop()
		}
	}()
	<-restart
}
